#include "napi/native_api.h"
#include "utils.cpp"
#include <cstdint>
#include <memory>
extern "C" {
#include "libavcodec/mediacodec.h"
#include "libavformat/avformat.h"
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
#include "libswresample/swresample.h"
}

static napi_value NAPI_Global_getMediaFormatStreamInfo(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    size_t len = 0;
    napi_get_value_string_utf8(env, args[0], nullptr, 0, &len);
    const std::unique_ptr<char[]> file_path = std::make_unique<char[]>(len + 1);
    napi_get_value_string_utf8(env, args[0], file_path.get(), len + 1, &len);
    char *path = PathToUri(file_path.get());
    AVFormatContext *formatCtx = avformat_alloc_context();
    if (avformat_open_input(&formatCtx, path, nullptr, nullptr) != 0) {
        free(path);
        avformat_close_input(&formatCtx);
        return nullptr;
    }
    free(path);
    if (avformat_find_stream_info(formatCtx, nullptr) < 0) {
        avformat_close_input(&formatCtx);
        return nullptr;
    }
    const int64_t duration = formatCtx->duration;
    const int64_t bitrate = formatCtx->bit_rate;
    int bitsPerRawSample = 0;
    int sampleRate = 0;
    int channels = 0;
    for (int i = 0; i < formatCtx->nb_streams; i++) {
        if (formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            AVCodecParameters *codecParameters = formatCtx->streams[i]->codecpar;
            bitsPerRawSample = codecParameters->bits_per_raw_sample;
            sampleRate = codecParameters->sample_rate;
            channels = codecParameters->ch_layout.nb_channels;
            break;
        }
    }
    napi_value metadata = nullptr;
    napi_create_object(env, &metadata);
    AVDictionaryEntry *entry = nullptr;
    while ((entry = av_dict_get(formatCtx->metadata, "", entry, AV_DICT_IGNORE_SUFFIX))) {
        const char *key = entry->key;
        const char *value = entry->value;

        napi_value keyKey = nullptr;
        napi_create_string_utf8(env, key, NAPI_AUTO_LENGTH, &keyKey);

        napi_value valueValue = nullptr;
        napi_create_string_utf8(env, value, NAPI_AUTO_LENGTH, &valueValue);
        napi_set_property(env, metadata, keyKey, valueValue);
    }
    napi_value coverByteArray = nullptr;
    for (int i = 0; i < formatCtx->nb_streams; ++i) {
        if (formatCtx->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC) {
            const AVPacket &packet = formatCtx->streams[i]->attached_pic;
            const int length = packet.size;
            void *buffer_data;
            napi_value arraybuffer;
            napi_create_arraybuffer(env, length, &buffer_data, &arraybuffer);
            memcpy(buffer_data, packet.data, length);
            napi_create_typedarray(env, napi_uint8_array, length, arraybuffer, 0, &coverByteArray);
            break;
        }
    }
    return CreateMetadata(env, duration, bitrate, metadata, bitsPerRawSample, sampleRate, channels, coverByteArray);
}

static napi_value NAPI_Global_getMediaReSampleArray(napi_env env, napi_callback_info info) {
    size_t argc = 4;
    napi_value args[4] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_value global;
    napi_get_global(env, &global);

    size_t len = 0;
    napi_get_value_string_utf8(env, args[1], nullptr, 0, &len);
    const std::unique_ptr<char[]> file_path = std::make_unique<char[]>(len + 1);
    napi_get_value_string_utf8(env, args[1], file_path.get(), len + 1, &len);

    int dst_sample_rate = -1;
    napi_get_value_int32(env, args[2], &dst_sample_rate);
    if (dst_sample_rate == -1) {
        napi_throw_error(env, NULL, "错误的采样率");
        return nullptr;
    }
    enum AVSampleFormat dst_sample_fmt = AV_SAMPLE_FMT_NONE;
    int sample_fmt;
    napi_get_value_int32(env, args[3], &sample_fmt);
    switch (sample_fmt) {
    case 0:
        dst_sample_fmt = AV_SAMPLE_FMT_U8;
        break;
    case 1:
        dst_sample_fmt = AV_SAMPLE_FMT_S16;
        break;
    }

    char *path = PathToUri(file_path.get());
    // 初始化FFmpeg相关上下文
    AVFormatContext *fmt_ctx = NULL;
    AVCodecContext *codec_ctx = NULL;
    const AVCodec *codec = NULL;
    SwrContext *swr_ctx = NULL;
    int audio_stream_index = -1;

    // 1. 打开输入文件
    if (avformat_open_input(&fmt_ctx, path, NULL, NULL) < 0) {
        free(path);
        napi_throw_error(env, NULL, "Could not open file");
        return nullptr;
    }
    free(path);

    // 2. 获取流信息
    if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
        napi_throw_error(env, NULL, "Could not find stream information");
        avformat_close_input(&fmt_ctx);
        return nullptr;
    }

    // 3. 查找音频流
    audio_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0);
    if (audio_stream_index < 0) {
        napi_throw_error(env, NULL, "Could not find audio stream");
        avformat_close_input(&fmt_ctx);
        return nullptr;
    }

    // 4. 初始化解码器
    codec_ctx = avcodec_alloc_context3(codec);
    avcodec_parameters_to_context(codec_ctx, fmt_ctx->streams[audio_stream_index]->codecpar);
    codec_ctx->thread_type = FF_THREAD_FRAME;
    codec_ctx->thread_count = 4;
    if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
        napi_throw_error(env, NULL, "Could not open codec");
        avformat_close_input(&fmt_ctx);
        avcodec_free_context(&codec_ctx);
        return nullptr;
    }

    // 5. 初始化重采样器（使用实际音频参数）
    swr_ctx = swr_alloc();
    av_opt_set_int(swr_ctx, "in_sample_rate", codec_ctx->sample_rate, 0);
    av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", codec_ctx->sample_fmt, 0);
    av_opt_set_chlayout(swr_ctx, "in_chlayout", &codec_ctx->ch_layout, 0);

    av_opt_set_int(swr_ctx, "out_sample_rate", dst_sample_rate, 0);
    av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", dst_sample_fmt, 0);
    const AVChannelLayout dst_ch_layout = AV_CHANNEL_LAYOUT_STEREO;
    av_opt_set_chlayout(swr_ctx, "out_chlayout", &dst_ch_layout, 0);
    swr_init(swr_ctx);

    // 6. 准备帧和包
    AVPacket *pkt = av_packet_alloc();
    AVFrame *frame = av_frame_alloc();
    uint8_t **dst_data = NULL;
    int dst_linesize;
    int max_dst_nb_samples = 0;

    int64_t duration_us = fmt_ctx->duration; // 单位：微秒
    double total_duration_sec = duration_us / (double)AV_TIME_BASE;

    const int64_t total_dst_samples = total_duration_sec * dst_sample_rate;

    const int bytes_per_sample = av_get_bytes_per_sample(dst_sample_fmt);
    int64_t total_buffer_size = total_dst_samples * dst_ch_layout.nb_channels * bytes_per_sample;

    napi_value arraybuffer;
    void *buffer_data;
    napi_create_arraybuffer(env, total_buffer_size, &buffer_data, &arraybuffer);

    size_t buffer_offset = 0;
    // 7. 主处理循环
    bool is_cancel = false;
    while (!is_cancel && av_read_frame(fmt_ctx, pkt) >= 0) {
        if (pkt->stream_index != audio_stream_index) {
            av_packet_unref(pkt);
            continue;
        }

        // 发送包到解码器
        int ret = avcodec_send_packet(codec_ctx, pkt);
        av_packet_unref(pkt);
        if (ret < 0)
            continue;

        // 接收解码后的帧
        while (avcodec_receive_frame(codec_ctx, frame) >= 0) {
            // 计算目标样本数
            int dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, codec_ctx->sample_rate) + frame->nb_samples,
                                                dst_sample_rate, codec_ctx->sample_rate, AV_ROUND_UP);

            // 检查缓冲区大小
            if (dst_nb_samples > max_dst_nb_samples) {
                av_samples_alloc_array_and_samples(&dst_data, &dst_linesize, dst_ch_layout.nb_channels, dst_nb_samples,
                                                   dst_sample_fmt, 0);
                max_dst_nb_samples = dst_nb_samples;
            }

            napi_value is_cancel_function;
            napi_call_function(env, global, args[0], 0, nullptr, &is_cancel_function);
            napi_get_value_bool(env, is_cancel_function, &is_cancel);
            if (is_cancel) {
                break;
            }

            // 执行重采样
            int converted =
                swr_convert(swr_ctx, dst_data, dst_nb_samples, (const uint8_t **)frame->data, frame->nb_samples);

            // 处理重采样后的数据（示例：获取buffer大小）
            int dst_buf_size =
                av_samples_get_buffer_size(&dst_linesize, dst_ch_layout.nb_channels, converted, dst_sample_fmt, 1);

            if (av_sample_fmt_is_planar(dst_sample_fmt)) {
                // Planar格式：合并多通道数据
                int bytes_per_sample = av_get_bytes_per_sample(dst_sample_fmt);
                for (int ch = 0; ch < dst_ch_layout.nb_channels; ch++) {
                    memcpy((uint8_t *)buffer_data + buffer_offset + ch * bytes_per_sample * converted, dst_data[ch],
                           bytes_per_sample * converted);
                }
                buffer_offset += dst_buf_size;
            } else {
                // Interleaved格式：直接拷贝
                memcpy((uint8_t *)buffer_data + buffer_offset, dst_data[0], dst_buf_size);
                buffer_offset += dst_buf_size;
            }
            // 清理帧引用
            av_frame_unref(frame);
        }
    }

    // 8. 清理资源
    av_packet_free(&pkt);
    av_frame_free(&frame);
    swr_free(&swr_ctx);
    avcodec_free_context(&codec_ctx);
    avformat_close_input(&fmt_ctx);
    av_freep(&dst_data);
    if (is_cancel) {
        return nullptr;
    }
    return arraybuffer;
}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {{"getMediaFormatStreamInfo", nullptr, NAPI_Global_getMediaFormatStreamInfo,
                                        nullptr, nullptr, nullptr, napi_default, nullptr},
                                       {"getMediaReSampleArray", nullptr, NAPI_Global_getMediaReSampleArray, nullptr,
                                        nullptr, nullptr, napi_default, nullptr}};
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "oal",
    .nm_priv = ((void *)0),
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void RegisterLibraryModule(void) { napi_module_register(&demoModule); }
